<?php
/*
 +-----------------------------------------------
 |大屏控制
 +-----------------------------------------------
 | Auth: dark2light@qq.com
 | Date: 2017.3.13
 +-----------------------------------------------
 |Main: 控制大屏上的显示数据，多数是接口，返回数据
 +-----------------------------------------------
*/
namespace Tug\Controller;

use Plume\Core\Controller;
use Plume\Core\Service;
use Plume\Core\Dao;
use Tug\Utils\InputUtils;
use Plume\Util\HttpUtils;

class ScreenController extends Controller
{
    private $redis;
    private $user;
    private $userGameResult;
    private $gameSetService;
    private $teamsService;
    private $gameStatusService;
    public function __construct($app) {
        parent::__construct($app);
        $this->userGameResult = new Service($app, new Dao($app, 'user_game_result'));
        $this->user = new Service($app, new Dao($app, 'user'));
        $this->gameStatusService = new Service($app, new Dao($app, 'game_status'));
        $this->gameSetService = new Service($app, new Dao($app, 'game_set'));
        $this->teamsService = new Service($app, new Dao($app, 'teams'));
        $this->redis = new \Redis();
        $redisInfo = $this->getConfigValue('redis');
        if (!$this->redis->connect($redisInfo['host'], $redisInfo['port'])) {
            file_put_contents('redis_error.txt', date('Y-m-d H:i:s').'redis连接错误！'.PHP_EOL, 8);
        }
    }

    public function gameAction() {

    }
    // 游戏准备状态，大屏轮询调用。返回红队和蓝队有多少人加入游戏
    public function headCountAction()
    {
        try {
            $must = ['meet_id', 'sequence'];
            list($meetId, $sequence) = InputUtils::issetOrFail('post', $must);
            $roundKey = "game_round:{$meetId}_{$sequence}";
            $round = $this->redis->get($roundKey);
            $joinRedKey =  "user_red:{$sequence}_{$round}";
            $joinBlueKey =  "user_blue:{$sequence}_{$round}";
            $red = $this->redis->zCard($joinRedKey);
            $blue = $this->redis->zCard($joinBlueKey);
            $bkDt = ['lNum' => $red, 'rNum' => $blue];
        } catch (\Exception $e) {
            $bkDt = ['errCode' => -100, 'msg' => $e->getMessage()];
        }
        exit(json_encode($bkDt));
    }
    // 大屏等待页获取游戏设置信息。
    public function waitAction()
    {
        try {
            $redis = $this->redis;
            $must = ['meet_id', 'sequence'];
            list($meetId, $sequence) = InputUtils::issetOrFail('post', $must);

            $gameInfoKey = "game_set:{$meetId}_{$sequence}";
            $gameInfo = $redis->hGet($gameInfoKey, 'sequence');
            $roundKey = "game_round:{$meetId}_{$sequence}";
            if ($gameInfo == false) {  // 如果Redis中没有游戏数据
                $where = ['sequence' => $sequence];
                $gameInfo =$this->gameSetService->fetch($where);
                if (!empty($gameInfo)) { // 如果游戏被设置过。并且Redis中没有数据！
                    $teamInfo = $this->teamsService->fetch($where);
                    $gameRound = "select max(round) as round from game_status where sequence = '{$sequence}'";
                    $gameRound = $this->gameStatusService->queryBySql($gameRound);
                    $gameRound = isset($gameRound[0]['round']) ? $gameRound[0]['round']+1 : 1;
                    $gameStatusInfoKey = "game_status:{$meetId}_{$sequence}_{$gameRound}";
                    $gameStatusInfo = [
                        'sequence' => $sequence, 'status' => 1, 'win' => 0,
                        'blue_score'=> '0', 'red_score' => 0, 'round' => $gameRound
                    ];
                    $redTeamInfo = [];  $blueTeamInfo = [];
                    foreach ($teamInfo as $team) {
                        if ($team['type'] == 1) {
                            $redTeamInfo = $team;
                        } else {
                            $blueTeamInfo = $team;
                        }
                    }
                    $redTeamInfoKey = "teams:{$sequence}_1";
                    $blueTeamInfoKey = "teams:{$sequence}_2";
                    $gameInfoKey = "game_set:{$meetId}_{$sequence}";
                    $redis->multi();
                    $bool1 = $redis->hMset($redTeamInfoKey, $redTeamInfo);
                    $bool2 =  $redis->hMset($blueTeamInfoKey, $blueTeamInfo);
                    $bool3 = $redis->hMset($gameInfoKey, $gameInfo[0]);
                    $bool4 = $redis->hMset($gameStatusInfoKey, $gameStatusInfo);
                    $bool5 = $redis->set($roundKey, $gameRound);
                    // 设置Redis中的失效时间为第二天凌晨三点
                    $time = time();
                    $year = date("Y", $time);
                    $month = (int)date("m", $time);
                    $day = date("d", $time);
                    $expire = mktime(5,0,0,$month,$day+1,$year)-time();
                    $redis->expire($redTeamInfoKey, $expire);
                    $redis->expire($blueTeamInfoKey, $expire);
                    $redis->expire($gameInfoKey, $expire);
                    $redis->expire($gameStatusInfoKey, $expire);
                    $redis->expire($roundKey, $expire);
                    if ($bool1  && $bool2 && $bool3 && $bool4 && $bool5){
                        $redis->exec();
                    } else {
                        $redis->discard();
                        $bkData = ['reCode' => -1, 'msg' => '不存在的拔河，请到环节设置中设置。'];
                        exit(json_encode($bkData));
                    }
                }
            }
            $gameInfoKey = "game_set:{$meetId}_{$sequence}";
            $redTeamInfoKey = "teams:{$sequence}_1";
            $blueTeamInfoKey = "teams:{$sequence}_2";
            $gameInfo = $this->redis->hGetAll($gameInfoKey);
            $redTeamInfo = $this->redis->hGetAll($redTeamInfoKey);
            $blueTeamInfo = $this->redis->hGetAll($blueTeamInfoKey);
            $roundKey = "game_round:{$meetId}_{$sequence}";
            $gameInfo['round'] = $this->redis->get($roundKey);
            if (empty($gameInfo)) {
                $bkData = ['reCode' => 0, 'msg' => '不存在的拔河，请到环节设置中设置。'];
            } else {
                $bkData =[
                    'gameInfo' => $gameInfo, 'redInfo' =>$redTeamInfo,
                    'blueInfo' => $blueTeamInfo ,'reCode' => 1,'end' => 999, 'mf' => 1
                ];
            }
        } catch (\Exception $e) {
            $bkData = ['code' => -1, 'msg' => $e->getMessage()];
        }
        echo json_encode($bkData);exit;
    }
    // 游戏开始，检查游戏状态，根据状态做出响应。
    public function initialAction()
    {
        try {
            $redis = $this->redis;
            $must = ['meet_id', 'sequence', 'status'];
            list($meetId, $sequence) = InputUtils::issetOrFail('post', $must);
            $roundKey = "game_round:{$meetId}_{$sequence}";
            $round = $this->redis->get($roundKey);
            $joinRedKey =  "user_red:{$sequence}_{$round}";
            $joinBlueKey =  "user_blue:{$sequence}_{$round}";
            $joinRed = $redis->zCard($joinRedKey);
            $joinBlue = $redis->zCard($joinBlueKey);
            if($joinBlue + $joinRed < 2) { // 如果人数不为零并且 红蓝人数相同
                $bkDt = ['res' => -3, 'msg' => '人数不足'];
            } else {
                if ($joinRed > $joinBlue) {
                    $redis->zRemRangeByRank($joinRedKey,$joinBlue, -1);
                    $bkDt = ['res' => 1, 'msg' => '裁剪红队人数成功，游戏可以开始'];
                } else if ($joinRed < $joinBlue){
                    $redis->zRemRangeByRank($joinRedKey,$joinRed, -1);
                    $bkDt = ['res' => 1, 'msg' => '裁剪蓝队人数成功，游戏可以开始'];
                } else {
                    $bkDt = ['res' => 1, 'msg' => '红队==蓝队'];
                }
            }
        } catch (\Exception $e) {
            $bkDt = ['res' => -2, 'msg' => $e->getMessage()];
        }
        exit (json_encode($bkDt));
    }
    // 大屏获取游戏剩余时间
    public function getTimeAction()
    {
        $redis = $this->redis;
        $must = ['meet_id', 'sequence'];
        list($meetId, $sequence) = InputUtils::issetOrFail('post', $must);
        $roundKey = "game_round:{$meetId}_{$sequence}";
        $round = $redis->get($roundKey);
        $gameStatusInfoKey = "game_status:{$meetId}_{$sequence}_$round";
        $overTime = $redis->hGet($gameStatusInfoKey, 'overTime');
        $now = time();
        if ($now <= $overTime) {
            $bkDt = ['code' => 1, 'time' => (int)$overTime  - $now, 'msg' => '获取时间成功'];
        } else {
            $redPower = 0;  $bluePower = 0;
            $joinRedKey = "user_red:{$sequence}_{$round}";
            $joinBlueKey = "user_blue:{$sequence}_{$round}";
            $redUserOpenid = $redis->zRange($joinRedKey, 0, -1);
            $blueUserOpenId = $redis->zRange($joinBlueKey, 0, -1);
            foreach ($redUserOpenid as $openid) {
                $userGameResultKey = "user_game_result:{$sequence}_{$openid}_{$round}";
                $redPower += (int)$redis->hGet($userGameResultKey, 'total');
            }
            foreach ($blueUserOpenId as $openid) {
                $userGameResultKey = "user_game_result:{$sequence}_{$openid}_{$round}";
                $bluePower += (int)$redis->hGet($userGameResultKey, 'total');
            }
            if ($redPower == $bluePower) {
                $win = 0;
            } else if ($redPower > $bluePower) {
                $win = 1;
            } else {
                $win = 2;
            }
            $set = ['status' => 3, 'blue_score' => $bluePower, 'red_score' => $redPower, 'win' => $win];
            $bool = $redis->hMset($gameStatusInfoKey, $set);
            if ($bool) {
                $bkDt = ['code' => -1, 'status' => 1, 'msg' => '游戏结束成功!'];
            } else {
                $bkDt = ['res' => -1, 'status' => 0, 'msg' => '游戏结束失败!'];
            }
        }
        exit(json_encode($bkDt));
    }
    // 游戏结果保存到mongodb,游戏如果结束那么触发保存数据到Mysql动作
    private function saveToMongodb($statistic)
    {
        $url = $this->getConfigValue('analysis_url');
        foreach($statistic as $item_data){
            $result = HttpUtils::http_post($url, $item_data);
            $json = json_decode($result['content'],true);
            if($json['code']!=0) {
                $this->log('mongodb_error', json_encode($statistic));
            }
        }
    }
    // 游戏结束数据存库
    private  function saveGameData($meetId, $sequence, $round)
    {
        $redis = $this->redis;
        $blueUserKey =  "user_blue:{$sequence}_{$round}";
        $redUserKey =  "user_red:{$sequence}_{$round}";
        $joinRed = $redis->zRange($redUserKey, 0, -1);
        $joinBlue = $redis->zRange($blueUserKey, 0, -1);
        $player = array_merge($joinBlue, $joinRed);
        $gameStatusInfoKey = "game_status:{$meetId}_{$sequence}_$round";
        $gameStatusInfo = $redis->hGetAll($gameStatusInfoKey);
        $this->user->beginTransaction();
        $bool = $this->gameStatusService->insert($gameStatusInfo);
        foreach ($player as  $openid){
            $userInfoKey = "user:{$openid}";
            $userGameResultKey = "user_game_result:{$sequence}_{$openid}_{$round}";
            $userInfo = $redis->hGetAll($userInfoKey);
            if (!isset($userInfo['isInsertToMysql'])) {
                $this->user->insert($userInfo);     // 进一步优化，相同的数据只存储一次。
                $redis->hSet($userInfoKey, 'isInsertToMysql', 1);
            }
            $userGameResult = $redis->hGetAll($userGameResultKey);
            $bool1 = $this->userGameResult->insert($userGameResult);
            if ($bool1 && $bool) {
                $this->user->commitTransaction();
            } else {
                $this->log('数据存储到数据库失败！', $userInfo);
                $this->user->rollbackTransaction();
                return false;
            }
        }
        return true;
    }
    // 返回蓝队和红队贡献的力量。
    public function tuggingAction()
    {
        try {
            $redis = $this->redis;
            $must = ['meet_id', 'sequence'];
            list($meetId, $sequence) = InputUtils::issetOrFail('post', $must);
            $roundKey = "game_round:{$meetId}_{$sequence}";
            $round = $redis->get($roundKey);
            $gameStatusInfoKey = "game_status:{$meetId}_{$sequence}_$round";
            $gameStatus = $redis->hGet($gameStatusInfoKey, 'status');
            $redPower = 0; $bluePower = 0;
            if($gameStatus !== false){
                $joinRedKey =  "user_red:{$sequence}_{$round}";
                $joinBlueKey =  "user_blue:{$sequence}_{$round}";
                $redUserOpenid = $redis->zRange($joinRedKey, 0, -1);
                $blueUserOpenId = $redis->zRange($joinBlueKey, 0, -1);
                foreach($redUserOpenid as $openid) {
                    $userGameResultKey = "user_game_result:{$sequence}_{$openid}_{$round}";
                    $redPower  += (int)$redis->hGet($userGameResultKey, 'total');
                }
                foreach($blueUserOpenId as $openid) {
                    $userGameResultKey = "user_game_result:{$sequence}_{$openid}_{$round}";
                    $bluePower  += (int)$redis->hGet($userGameResultKey, 'total');
                }
                $bkDt = ['res' => 1, 'left' => $redPower, 'right' => $bluePower];
            } else {
                $bkDt = ['res' => -100, 'msg' => '不存在的游戏！'];
            }
        } catch (\Exception $e) {
            $bkDt = ['res' => -2, 'msg' => $e->getMessage()];
        }
        exit(json_encode($bkDt));
    }
    // 游戏开始后，获取红队和蓝队的用户信息
    public function startAction()
    {
        try {
            $redis = $this->redis;
            $must = ['meet_id', 'sequence', 'round'];
            list($meetId, $sequence, $round) = InputUtils::issetOrFail('post', $must);
            $gameStatusInfoKey = "game_status:{$meetId}_{$sequence}_{$round}";
            $gameStatus = $redis->hGet($gameStatusInfoKey,'status');
            if($gameStatus !== false) {
                $joinRedKey =  "user_red:{$sequence}_{$round}";
                $joinBlueKey =  "user_blue:{$sequence}_{$round}";
                $redUserOpenid = $this->redis->zRange($joinRedKey, 0, -1);
                $blueUserOpenId = $this->redis->zRange($joinBlueKey, 0, -1);
                $redUserInfo = [];
                foreach ($redUserOpenid as  $openid){
                    $userInfoKey = "user:{$openid}";
                    $redUserInfo[] = $redis->hGetAll($userInfoKey);
                }
                $blueUserInfo = [];
                foreach ($blueUserOpenId as  $openid){
                    $userInfoKey = "user:{$openid}";
                    $blueUserInfo[] = $redis->hGetAll($userInfoKey);
                }
                $players = $redis->zCard($joinRedKey); // 有多少人玩游戏 red*2
                $bkDt = [
                    'res' => 1, 'msg' => '获取成功！！', 'players' => $players,
                    'l_u' => $redUserInfo, 'r_u' => $blueUserInfo
                ];
            } else {
                $bkDt = ['res' => -100, 'msg' => '游戏数据不存在'];
            }
        } catch (\Exception $e) {
            $bkDt = ['res' => -2, 'msg' => $e->getMessage()];
        }
        exit(json_encode($bkDt, true));
    }
    // 游戏结果统计，通知mongodb存储
    public function statisticAction()
    {
        try {
            $redis = $this->redis;
            $must = ['meet_id', 'sequence'];
            list($meetId, $sequence) = InputUtils::issetOrFail('post', $must);
            $roundKey = "game_round:{$meetId}_{$sequence}";
            $round = $redis->get($roundKey); // 游戏进行到第几轮
            $gameStatusInfoKey = "game_status:{$meetId}_{$sequence}_$round";
            $gameStatus = $redis->hGet($gameStatusInfoKey, 'status');
            if ($gameStatus == 3) {
                $redTeamInfoKey = "teams:{$sequence}_1";
                $blueTeamInfoKey = "teams:{$sequence}_2";
                $redTeamInfo = $this->redis->hGetAll($redTeamInfoKey);
                $blueTeamInfo = $this->redis->hGetAll($blueTeamInfoKey);
                $gameStatusInfoKey = "game_status:{$meetId}_{$sequence}_{$round}";
                $gameStatusInfo = $redis->hGetAll($gameStatusInfoKey);
                $winTeamUsers = [];
                $joinRedKey =  "user_red:{$sequence}_{$round}";
                $redUserOpenid = $redis->zRange($joinRedKey, 0, -1);
                foreach($redUserOpenid as $openid) {
                    $userInfoKey = "user:{$openid}";
                    $userGameResultKey = "user_game_result:{$sequence}_{$openid}_{$round}";
                    $total = (int)$redis->hGet($userGameResultKey, 'total');
                    $temp = $redis->hMGet($userInfoKey, ['nickname', 'headimgurl']);
                    $redTeamUsers[] = $temp;
                    $statistic[] = array(
                        'meet_id' => $meetId, 'sequence' => $sequence,
                        'module_status' => 1, 'wx_image' => $temp['headimgurl'],
                        'wx_name' => $temp['nickname'], 'total' => $total,
                        'round' => $round, 'open_id' => $openid, 'team_id' => $redTeamInfo['type']);
                }
                $joinBlueKey =  "user_blue:{$sequence}_{$round}";
                $blueUserOpenId = $redis->zRange($joinBlueKey, 0, -1);
                foreach($blueUserOpenId as $openid) {
                    $userInfoKey = "user:{$openid}";
                    $userGameResultKey = "user_game_result:{$sequence}_{$openid}_{$round}";
                    $total = (int)$redis->hGet($userGameResultKey, 'total');
                    $temp = $redis->hMGet($userInfoKey, ['nickname', 'headimgurl']);
                    $blueTeamUsers[] = $temp;
                    $statistic[] = array(
                        'meet_id' => $meetId, 'sequence' => $sequence,
                        'module_status' => 1, 'wx_image' => $temp['headimgurl'],
                        'wx_name' => $temp['nickname'], 'total' => $total,
                        'round' => $round, 'open_id' => $openid, 'team_id' => $blueTeamInfo['type']
                    );
                }
                if ($gameStatusInfo['win'] == 1) {
                    $num = count($redUserOpenid);
                    $redWin = 1;
                    $blueWin = 0;
                    $winTeamUsers = $redTeamUsers;
                } else if($gameStatusInfo['win'] == 2){
                    $num = count($blueUserOpenId);
                    $redWin = 0;
                    $blueWin = 1;
                    $winTeamUsers = $blueTeamUsers;
                } else {
                    // 平局
                    $joinBlueKey =  "user_blue:{$sequence}_{$round}";
                    $blueUserOpenId = $redis->zRange($joinBlueKey, 0, -1);
                    $num = count($blueUserOpenId);
                    $redWin = 2;
                    $blueWin = 2;
                }
                $bkDt = [
                    'res' => 1, 'gameStatusInfo' => $gameStatusInfo,
                    'redTeamInfo' => $redTeamInfo, 'blueTeamInfo' => $blueTeamInfo,
                    'winTeamUsers' => $winTeamUsers
                ];
                $statistic[] = array(
                    'meet_id' => $meetId,'sequence' => $sequence,'module_status' => 0,
                    'team_id' => $redTeamInfo['type'], 'team_image' => $redTeamInfo['team_image'],
                    'team_name' => $redTeamInfo['team_name'], 'total' => $gameStatusInfo['red_score'], 'round' => $round,
                    'is_win' => $redWin, 'team_num' => $num);
                $statistic[] = array(
                    'meet_id' => $meetId,'sequence' => $sequence,'module_status' => 0,
                    'team_id' => $blueTeamInfo['type'] , 'team_image' => $blueTeamInfo['team_image'],
                    'team_name' =>$blueTeamInfo['team_name'], 'total' => $gameStatusInfo['blue_score'],
                    'round' => $round, 'is_win' => $blueWin, 'team_num' => $num);
                $isWritingKey = "isWriting:{$sequence}_$round";
                if ($isWriting = $redis->get($isWritingKey) != 1) {
                    $redis->set($isWritingKey, 1, 24*3600); // 写入锁
                }
                if ($isWriting != 1 && $this->saveGameData($meetId, $sequence, $round) != false) { // 游戏结束后将数据写会数据库
                    $this->saveToMongodb($statistic);
                }
            } else {
                $bkDt = ['res' => '-100', 'msg' => '游戏不存在 || 游戏正在进行中 '];
            }
        } catch (\Exception $e) {
            $bkDt = ['res' => -2, 'msg' => $e->getMessage()];
        }
        exit(json_encode($bkDt));
    }
    // 游戏结果页
    public function resultAction()
    {
        $server_url = $this->getConfigValue("server_url");
        return $this->result(['server_url' => $server_url])->response();
    }
    public function __destruct()
    {
        $this->redis->close();
    }
}